using Cysharp.Threading.Tasks;
using M13.Base;
using M13.GameSystem;
using M13.Math;
using M13.Utility;
using QFramework;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace M13.Game.World
{
    public enum ChunkInitStatus
    {
        ChunkInitUpdaterInitState,
        RegionDataInitState,
        ChunkDataInitState,
        ChunkObjInitState,
        ChunkInitFinished
    }

    public class ChunkInitUpdater : ICanGetSystem, ICanSendEvent
    {
        public Vector2Int[] RegionDataInitTasks { get; private set; } = new Vector2Int[9];
        public Vector2Int[] ChunkDataInitTasks { get; private set; } = new Vector2Int[49];
        public Vector2Int[] ChunkObjInitTasks { get; private set; } = new Vector2Int[25];
        
        public List<Vector2Int> LoadedChunkTags { get; private set; } = new List<Vector2Int>();
        public List<Vector2Int> LoadedRegionTags { get; private set; } = new List<Vector2Int>();

        public float Progress { get; private set; } = 0f;
        private float _progressCap = 0;
        public Dictionary<Vector2Int, Region> InitedRegionMap { get; private set; } = new Dictionary<Vector2Int, Region>();
        public Dictionary<Vector2Int, GameObject> ChunkObjMap { get; private set; } = new Dictionary<Vector2Int, GameObject>();

        private int _initChunkDataRange = 3;
        private int _initChunkObjRange = 2;
        private int _initRegionRange = 1;

        private ISaveSystem _saveSystem;

        Vector2Int _chunkLocalCoordPlayerIn;
        Vector2Int _regionLocalCoordPlayerIn;
        ChunkInitStatus _chunkInitStatus = ChunkInitStatus.ChunkInitUpdaterInitState;

        public ChunkInitUpdater(Vector2 playerV2Pos, int loadRange)
        {
            _saveSystem = this.GetSystem<ISaveSystem>();

            _chunkLocalCoordPlayerIn = new Vector2Int((int)playerV2Pos.x / 16, (int)playerV2Pos.y / 16);

            _chunkInitStatus = ChunkInitStatus.RegionDataInitState;
            _progressCap = RegionDataInitTasks.Length + ChunkDataInitTasks.Length + ChunkObjInitTasks.Length;
        }

        public void Init(SaveConfig saveConfig, Transform transform)
        {
            if (_chunkInitStatus == ChunkInitStatus.ChunkInitFinished)
            {
                this.SendEvent<InitWorldFinished>();
                return;
            }
            RegionDataInit(saveConfig);
            ChunkDataInit();
            ChunkObjInit(transform);
            Progress = (_rdIndex + _cdIndex + _coIndex) / _progressCap;
            Debug.Log($"�����ʼ�����ȣ�{Progress}");
        }

        int _rdIndex = 0;
        private async void RegionDataInit(SaveConfig saveConfig)
        {
            if ((int)_chunkInitStatus > 1) return;
            if (_rdIndex >= RegionDataInitTasks.Length)
            {
                _chunkInitStatus = ChunkInitStatus.ChunkDataInitState;
                return;
            }
            await UniTask.Yield();
            Region region = RegionDataParser.RegionDecompress(_saveSystem, saveConfig, RegionDataInitTasks[_rdIndex]);
            InitedRegionMap.Add(RegionDataInitTasks[_rdIndex], region);
            LoadedRegionTags.Add(RegionDataInitTasks[_rdIndex]);
            _rdIndex++;
        }

        int _cdIndex = 0;
        private async void ChunkDataInit()
        {
            if ((int)_chunkInitStatus > 2) return;
            if (_cdIndex >= ChunkDataInitTasks.Length)
            {
                _chunkInitStatus = ChunkInitStatus.ChunkObjInitState;
                return;
            }
            await UniTask.Yield();
            foreach(var r in InitedRegionMap)
            {
                if (r.Value.IsChunkOnThisRegion(ChunkDataInitTasks[_cdIndex]))
                {
                    r.Value.AcquireChunkloadTesk(ChunkDataInitTasks[_cdIndex]);
                    LoadedChunkTags.Add(ChunkDataInitTasks[_cdIndex]);
                    _cdIndex++;
                    return;
                }
            }
        }

        int _coIndex = 0;
        private async void ChunkObjInit(Transform transform)
        {
            if ((int)_chunkInitStatus > 3) return;
            if (_coIndex >= ChunkObjInitTasks.Length)
            {
                _chunkInitStatus = ChunkInitStatus.ChunkInitFinished;
                return;
            }
            await UniTask.Yield();
            if (ChunkObjMap.Keys.Contains(ChunkObjInitTasks[_coIndex])) return;

            if (!LoadedChunkTags.Contains(ChunkObjInitTasks[_coIndex] + Vector2Int.left)
                || !LoadedChunkTags.Contains(ChunkObjInitTasks[_coIndex] + Vector2Int.up)
                || !LoadedChunkTags.Contains(ChunkObjInitTasks[_coIndex] + Vector2Int.right)
                || !LoadedChunkTags.Contains(ChunkObjInitTasks[_coIndex] + Vector2Int.down))
            {
                return;
            }
            
            List<byte[]> neighborChunkHeightMaps = new List<byte[]>();
            byte[] chm = (InitedRegionMap[M13Mathf.ChunkToRegionCoord(ChunkObjInitTasks[_coIndex])].ChunkMap[ChunkObjInitTasks[_coIndex]].ChunkHeightMap);
            byte[] ctim = (InitedRegionMap[M13Mathf.ChunkToRegionCoord(ChunkObjInitTasks[_coIndex])].ChunkMap[ChunkObjInitTasks[_coIndex]].ChunkTurfIdMap);

            try
            {
                neighborChunkHeightMaps.Add((InitedRegionMap[M13Mathf.ChunkToRegionCoord(ChunkObjInitTasks[_coIndex] + Vector2Int.left)].ChunkMap[ChunkObjInitTasks[_coIndex] + Vector2Int.left].ChunkHeightMap));
                neighborChunkHeightMaps.Add((InitedRegionMap[M13Mathf.ChunkToRegionCoord(ChunkObjInitTasks[_coIndex] + Vector2Int.up)].ChunkMap[ChunkObjInitTasks[_coIndex] + Vector2Int.up].ChunkHeightMap));
                neighborChunkHeightMaps.Add((InitedRegionMap[M13Mathf.ChunkToRegionCoord(ChunkObjInitTasks[_coIndex] + Vector2Int.right)].ChunkMap[ChunkObjInitTasks[_coIndex] + Vector2Int.right].ChunkHeightMap));
                neighborChunkHeightMaps.Add((InitedRegionMap[M13Mathf.ChunkToRegionCoord(ChunkObjInitTasks[_coIndex] + Vector2Int.down)].ChunkMap[ChunkObjInitTasks[_coIndex] + Vector2Int.down].ChunkHeightMap));
            }
            catch
            {
                Debug.LogError($"�������{ChunkObjInitTasks[_coIndex]}���������黹δ��ȫ����");
            }

            ChunkMesh cmesh = new ChunkMesh(chm, neighborChunkHeightMaps, ctim);
            ChunkObjMap.Add(ChunkObjInitTasks[_coIndex], ChunkObject.ChunkInstantiate(ChunkObjInitTasks[_coIndex], cmesh, transform));

            _coIndex++;
        }

        public IArchitecture GetArchitecture()
        {
            return GameArchitecture.Interface;
        }
    }
}